using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Pacman.Simulator;
using NeuralNetwork;
using System.Drawing;

namespace Pacman.Implementations
{
	public class NeuralPredictionPac : BasePacman
	{
		public NeuralPredictionPac() : base("NeuralPredictionPac","1") { }

		private Network network;
		private const bool debug = false;
		private GameState gs;

		public override void Draw(System.Drawing.Graphics g) {
			Prediction prediction = new Prediction(gs, 9);
			for( int x = 0; x < Map.Width; x++ ) {
				for( int y = 0; y < Map.Height; y++ ) {
					Node node = gs.Map.Nodes[x, y];
					if( node.Walkable ) {
						g.FillEllipse(new SolidBrush(Color.FromArgb(255, (int)(prediction.DangerMaps[9].Danger[x, y] * 255), 0, 0)), new Rectangle(new Point(node.CenterX, node.CenterY), new Size(8, 8)));
					}
				}
			}
		}

		public override Direction Think(GameState gs) {
			if( debug ) Console.WriteLine(" ----- ");
			this.gs = gs;
			if( network == null ) {
				network = Network.Load("danger.nn");
			}
			// neural network input
			List<DirectionDanger> directionDanger = new List<DirectionDanger>();
			Prediction prediction = new Prediction(gs, 9);
			double[] inputs = new double[3];
			foreach( Direction possible in gs.Pacman.PossibleDirections() ) {
				int nodeCount = 0;
				double dangerCount = 0.0;
				List<DirectionNode> nodes = new List<DirectionNode>();
				nodes.Add(new DirectionNode(gs.Pacman.Node, possible));
				for( int i = 1; i < 10; i++ ) {
					List<DirectionNode> newNodes = new List<DirectionNode>();
					foreach( DirectionNode dn in nodes ) {
						foreach( Node dnPossible in dn.Node.GhostPossibles[(int)dn.Direction] ) {
							dangerCount += prediction.DangerMaps[i - 1].Danger[dnPossible.X, dnPossible.Y];
							nodeCount++;
							newNodes.Add(new DirectionNode(dnPossible, dn.Node.GetDirection(dnPossible)));
						}
					}
					if( i % 3 == 0 ) {
						//Console.WriteLine(possible + ": " + dangerCount + " / " + nodeCount + " = " + (dangerCount / nodeCount));
						inputs[i / 3 - 1] = dangerCount / nodeCount;
						nodeCount = 0;
						dangerCount = 0.0;
					}
					nodes = newNodes;
				}
				double danger = network.GetOutputs(inputs)[0];
				directionDanger.Add(new DirectionDanger(possible,danger));				
			}
			// add danger if close to ghost						
			foreach( Simulator.Ghosts.Ghost ghost in gs.Ghosts ) {
				if( !ghost.Chasing ) {
					continue;
				}
				Node.PathInfo shortestPath = gs.Pacman.Node.ShortestPath[ghost.Node.X, ghost.Node.Y];
				if( shortestPath != null ) {					
					int danger = 5 - shortestPath.Distance;
					if( danger < 1 ) {
						continue;
					}
					foreach( DirectionDanger dd in directionDanger ) {
						if( dd.Direction == shortestPath.Direction ) {
							dd.Danger += (danger / 10.0);
						}
					}
				}
			}
			// add danger to reverse direction
			foreach( DirectionDanger dd in directionDanger ) {
				if( StateInfo.IsInverse(dd.Direction, gs.Pacman.Direction) ) {
					dd.Danger += 0.15;
				}
			}
			// calc lowest danger
			double lowestDanger = double.MaxValue;
			foreach( DirectionDanger dd in directionDanger ) {
				if( debug ) Console.WriteLine(dd.Direction + ": " + dd.Danger);
				if( dd.Danger < lowestDanger ) {
					lowestDanger = dd.Danger;
				}
			}
			// sort
			directionDanger.Sort(new Comparison<DirectionDanger>(delegate(DirectionDanger dd1, DirectionDanger dd2) {
				if( dd1.Danger == dd2.Danger ) return 0;
				if( dd1.Danger < dd2.Danger ) return -1;
				return 1;
			}));
			// least dangerous
			Direction best = directionDanger[0].Direction;
			// try hunt pills
			StateInfo.PillPath pillPath = StateInfo.NearestPill(gs.Pacman.Node, gs);
			if( pillPath.PathInfo != null ) {
				foreach( DirectionDanger dd in directionDanger ) {
					if( dd.Direction == pillPath.PathInfo.Direction && dd.Danger - lowestDanger < 0.1 && dd.Danger < 0.3 ) {
						best = pillPath.PathInfo.Direction;
					}
				}
			}
			// return best
			if( debug ) Console.WriteLine("Go: " + best);
			return best;
		}

		public override void EatenByGhost() {
			if( debug ) Console.WriteLine(" ------------ PACMAN WAS EATEN ALIVE ------------ ");
		}

		private class DirectionDanger
		{
			public readonly Direction Direction;
			public double Danger;

			public DirectionDanger(Direction direction, double danger) {
				this.Direction = direction;
				this.Danger = danger;
			}
		}

		private class DirectionNode
		{
			public readonly Node Node;
			public readonly Direction Direction;

			public DirectionNode(Node node, Direction direction) {
				this.Node = node;
				this.Direction = direction;
			}
		}
	}
}
